#!/bin/bash -u

# Released into the Public Domain.
#
# Original implementation in C by Brad Conte (brad@bradconte.com) <https://github.com/B-Con/crypto-algorithms>
# Ported to Bash (lol) by Josh Junon (josh@junon.me) <https://github.com/qix->
#
# Original gist is https://gist.github.com/Qix-/affef08b50686e54e1f2ca18f97a6ff7
# Removed external utilities.
#
# Yes, it's absolutely as slow as it looks.
#

export LANG=C

odbash()
{
	local c x
	while IFS= read -r -n 1 -d '' c; do
		printf -v x "%d" "'$c"
		printf '%4d' $(( $x & 0xff ))
	done
	printf '\n'
}

sha256()
{
	declare -a k data rhash bitlen state
	declare -i datalen=0

	k=( \
		$((0x428a2f98)) $((0x71374491)) $((0xb5c0fbcf)) $((0xe9b5dba5)) $((0x3956c25b)) $((0x59f111f1)) $((0x923f82a4)) $((0xab1c5ed5)) \
		$((0xd807aa98)) $((0x12835b01)) $((0x243185be)) $((0x550c7dc3)) $((0x72be5d74)) $((0x80deb1fe)) $((0x9bdc06a7)) $((0xc19bf174)) \
		$((0xe49b69c1)) $((0xefbe4786)) $((0x0fc19dc6)) $((0x240ca1cc)) $((0x2de92c6f)) $((0x4a7484aa)) $((0x5cb0a9dc)) $((0x76f988da)) \
		$((0x983e5152)) $((0xa831c66d)) $((0xb00327c8)) $((0xbf597fc7)) $((0xc6e00bf3)) $((0xd5a79147)) $((0x06ca6351)) $((0x14292967)) \
		$((0x27b70a85)) $((0x2e1b2138)) $((0x4d2c6dfc)) $((0x53380d13)) $((0x650a7354)) $((0x766a0abb)) $((0x81c2c92e)) $((0x92722c85)) \
		$((0xa2bfe8a1)) $((0xa81a664b)) $((0xc24b8b70)) $((0xc76c51a3)) $((0xd192e819)) $((0xd6990624)) $((0xf40e3585)) $((0x106aa070)) \
		$((0x19a4c116)) $((0x1e376c08)) $((0x2748774c)) $((0x34b0bcb5)) $((0x391c0cb3)) $((0x4ed8aa4a)) $((0x5b9cca4f)) $((0x682e6ff3)) \
		$((0x748f82ee)) $((0x78a5636f)) $((0x84c87814)) $((0x8cc70208)) $((0x90befffa)) $((0xa4506ceb)) $((0xbef9a3f7)) $((0xc67178f2)) \
	)
	state=( \
		$((0x6a09e667)) $((0xbb67ae85)) $((0x3c6ef372)) $((0xa54ff53a)) $((0x510e527f)) $((0x9b05688c)) $((0x1f83d9ab)) $((0x5be0cd19)) \
	)
	bitlen=( 0 0 )

	dbl_int_add()
	{
		if [ ${bitlen[0]} -gt $(( 0xffffffff - ${1} )) ]; then
			bitlen[1]=$(( ${bitlen[1]} + 1 ))
		fi
		bitlen[0]=$(( ${bitlen[0]} + ${1} ))
	}
	rotright()
	{
		eval $1=$(( (($2 >> $3) | ($2 << (32 - $3))) & 0xFFFFFFFF ))
	}
	ch()
	{
		declare -i a=$2 b=$3 c=$4 nota
		not32 nota $2

		eval $1=$(( ($a & $b) ^ ($nota & $c) ))
	}
	maj()
	{
		declare -i a=$2 b=$3 c=$4

		eval $1=$(( ($a & $b) ^ ($a & $c) ^ ($b & $c) ))
	}
	ep0()
	{
		declare -i a b c

		rotright a $2 2
		rotright b $2 13
		rotright c $2 22

		eval $1=$(( $a ^ $b ^ $c ))
	}
	ep1()
	{
		declare -i a b c

		rotright a $2 6
		rotright b $2 11
		rotright c $2 25

		eval $1=$(( $a ^ $b ^ $c ))
	}
	sig0()
	{
		declare -i a b

		rotright a $2 7
		rotright b $2 18

		eval $1=$(( $a ^ $b ^ ($2 >> 3) ))
	}
	sig1()
	{
		declare -i a b

		rotright a $2 17
		rotright b $2 19

		eval $1=$(( $a ^ $b ^ ($2 >> 10) ))
	}
	b32()
	{
		eval $1=$(( $2 & 0xFFFFFFFF ))
	}
	not32()
	{
		declare -i a

		b32 a $(( ~$2 ))

		eval $1=$a
	}
	sha256_transform()
	{
		declare -a m
		declare -i t1 t2 x1 x2

		for (( i=0; i < 16; i++ )) do
			declare -i j=$(($i * 4))
			b32 m[$i] $(( (${data[$j]} << 24) | (${data[$(($j + 1))]} << 16) | (${data[$(($j + 2))]} << 8) | ${data[$(($j + 3))]} ))
		done

		for (( i=16; i < 64; i++ )) do
			sig1 x1 ${m[$(($i - 2))]}
			sig0 x2 ${m[$(($i - 15))]}
			b32 m[$i] $(( $x1 + ${m[$(($i - 7))]} + $x2 + ${m[$(($i - 16))]} ))
		done

		declare -i a=${state[0]}
		declare -i b=${state[1]}
		declare -i c=${state[2]}
		declare -i d=${state[3]}
		declare -i e=${state[4]}
		declare -i f=${state[5]}
		declare -i g=${state[6]}
		declare -i h=${state[7]}
	
		for (( i=0; i < 64; i++ )) do
			ep1 x1 $e
			ch  x2 $e $f $g
			b32 t1 $(( $h + $x1 + $x2 + ${k[$i]} + ${m[$i]} ))

			ep0 x1 $a
			maj x2 $a $b $c
			b32 t2 $(( $x1 + $x2 ))

			h=$g
			g=$f
			f=$e
			b32 e $(( $d + $t1 ))
			d=$c
			c=$b
			b=$a
			b32 a $(( $t1 + $t2 ))
		done

		b32 state[0] $(( ${state[0]} + $a ))
		b32 state[1] $(( ${state[1]} + $b ))
		b32 state[2] $(( ${state[2]} + $c ))
		b32 state[3] $(( ${state[3]} + $d ))
		b32 state[4] $(( ${state[4]} + $e ))
		b32 state[5] $(( ${state[5]} + $f ))
		b32 state[6] $(( ${state[6]} + $g ))
		b32 state[7] $(( ${state[7]} + $h ))
	}

	local line byte

	while read -r line; do
		for byte in $line; do
			data[$datalen]=$byte
			datalen=$(( $datalen + 1 ))
			if [ $datalen -eq 64 ]; then
				sha256_transform
				dbl_int_add 512
				datalen=0
			fi
		done
	done < <(odbash)

	declare -i i=$datalen

	if [ $datalen -lt 56 ]; then
		data[$i]=$(( 0x80 ))
		i=$(( $i + 1 ))

		while [ $i -lt 56 ]; do
			data[$i]=0
			i=$(( $i + 1 ))
		done
	else
		data[$i]=$(( 0x80 ))
		i=$(( $i + 1 ))

		while [ $i -lt 64 ]; do
			data[$i]=0
			i=$(( $i + 1 ))
		done

		sha256_transform

		for (( j=0; j < 56; j++ )) do
			data[$j]=0
		done
	fi

	dbl_int_add $(( $datalen * 8 ))
	data[63]=$((  ${bitlen[0]}        & 0xFF ))
	data[62]=$(( (${bitlen[0]} >>  8) & 0xFF ))
	data[61]=$(( (${bitlen[0]} >> 16) & 0xFF ))
	data[60]=$(( (${bitlen[0]} >> 24) & 0xFF ))
	data[59]=$((  ${bitlen[1]}        & 0xFF ))
	data[58]=$(( (${bitlen[1]} >>  8) & 0xFF ))
	data[57]=$(( (${bitlen[1]} >> 16) & 0xFF ))
	data[56]=$(( (${bitlen[1]} >> 24) & 0xFF ))
	sha256_transform

	for (( j=0; j < 4; j++ )) do
		rhash[$j]=$(( (${state[0]} >> (24 - $j * 8)) & 0xff ))
		rhash[$(( $j + 4  ))]=$(( (${state[1]} >> (24 - $j * 8)) & 0xff ))
		rhash[$(( $j + 8  ))]=$(( (${state[2]} >> (24 - $j * 8)) & 0xff ))
		rhash[$(( $j + 12 ))]=$(( (${state[3]} >> (24 - $j * 8)) & 0xff ))
		rhash[$(( $j + 16 ))]=$(( (${state[4]} >> (24 - $j * 8)) & 0xff ))
		rhash[$(( $j + 20 ))]=$(( (${state[5]} >> (24 - $j * 8)) & 0xff ))
		rhash[$(( $j + 24 ))]=$(( (${state[6]} >> (24 - $j * 8)) & 0xff ))
		rhash[$(( $j + 28 ))]=$(( (${state[7]} >> (24 - $j * 8)) & 0xff ))
	done

	printf "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n" ${rhash[@]}
}

sha256
